﻿package twitter 
{
	import com.hurlant.crypto.Crypto;
	import com.hurlant.crypto.hash.HMAC;
	import com.hurlant.util.Base64;
	import com.hurlant.util.Hex;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.system.System;
	import flash.xml.*;

    import flash.net.*;
	import flash.events.Event;
	import flash.utils.ByteArray;
	
	import com.adobe.serialization.json.JSON;


	/**
	 * ...
	 * @author s
	 */
	public class AbstractTweetLoader
	{
		private var _accessToken:String;
		private var _accessTokenSecret:String;
		private var _consumerKey:String;
		private var _consumerSecret:String;
		private var _state: int;
		private var _tweets: Vector.<Tweet>;
		
		public static const STATE_IDLE:int = 0;
		public static const STATE_BUSY:int = 1;
		
		public function AbstractTweetLoader(accessToken:String, accessTokenSecret:String, consumerKey:String, consumerSecret:String)
		{
			_accessToken = accessToken;
			_accessTokenSecret = accessTokenSecret;
			_consumerKey = consumerKey;
			_consumerSecret = consumerSecret;
			_state = STATE_IDLE;
			_tweets = new Vector.<Tweet>;
		}
		
		protected function loadTimeline(forEachTweet:Function, url:String):Boolean 
		{
			if (_state != STATE_IDLE) {
				return false;
			}
			
			var type:String;
			type = url.substring(0, url.indexOf("?") >= 0 ? url.indexOf("?") : url.length);
			type = type.substring(type.lastIndexOf(".") + 1, type.length);
			
            var textLoader:URLLoader = new URLLoader();
            textLoader.addEventListener(Event.COMPLETE, function(e:Event):void {
				var tweets:Array;
				var tweet:Tweet;
				//trace(e.target.data.substr(0, 128));
				
				//forEachTweet(new Tweet("123", "hoge", "hogehoge", null, e.target.data));
				//return;
				
				if(type == "xml"){
					tweets = readTimelineXML(e.target.data)
				}
				else if (type == "json") {
					tweets = readTimelineJSON(e.target.data);
				}
				
				for each (tweet in tweets) {
					if(_tweets.length >= 1){
						_tweets[_tweets.length - 1].setNextTweet(tweet);
						tweet.setPrevTweet(_tweets[_tweets.length - 1]);
					}
					_tweets.push(tweet);
					forEachTweet(tweet);
				}
				_state = STATE_IDLE;
            });
			
			textLoader.addEventListener(IOErrorEvent.IO_ERROR, function (e:Event):void {
				_state = STATE_IDLE;
			} );
			
			textLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function ():void {
				_state = STATE_IDLE;
			}
			);
			
			var req:URLRequest = new URLRequest(url);
            textLoader.load(req);
			_state = STATE_BUSY;
			return true;
		}
		
		protected function readTimelineXML(text:String):Array 
		{
			System.useCodePage = true;
			default xml namespace = new Namespace("http://www.w3.org/2005/Atom");
			XML.ignoreWhitespace = true;
            var xml:XML = XML(text);
			var tweets:Array = [];

			for each (var status:XML in xml.status) {
				var date_buf:String = status.created_at;
				var date_ary:Array = date_buf.split(" ");
				var year:String = date_ary[5];
				var month:String = date_ary[1];
				var date:String = date_ary[2];
				var hour:String = date_ary[3].split(":")[0];
				var minute:String = date_ary[3].split(":")[1];
				var second:String = date_ary[3].split(":")[2];
				
				var createdAt:Date = new Date();
				createdAt.setTime(Date.parse(status.created_at));
				var tweet:Tweet = new Tweet(
					status.id,
					status.user.id,
					status.user.screen_name,
					createdAt,
					status.text
				);
				tweets.push(tweet);
			}
			
			return tweets.reverse();
		}
		
		
		protected function readTimelineJSON(text:String):Array 
		{
			var json:Array = JSON.decode(text);
			var tweets:Array = [];

			for each (var status:Object in json) {
				var date_buf:String = status["created_at"];
				var date_ary:Array = date_buf.split(" ");
				var year:String = date_ary[5];
				var month:String = date_ary[1];
				var date:String = date_ary[2];
				var hour:String = date_ary[3].split(":")[0];
				var minute:String = date_ary[3].split(":")[1];
				var second:String = date_ary[3].split(":")[2];
				
				var createdAt:Date = new Date();
				createdAt.setTime(Date.parse(status.created_at));
				var tweet:Tweet = new Tweet(
					status["id"],
					status["user"]["id"],
					status["user"]["screen_name"],
					createdAt,
					status["text"]
				);
				tweets.push(tweet);
			}
			
			return tweets.reverse();
		}


		protected function getSignedURI( method:String, url:String, postData:String = "" ):String
		{
			if( method.toUpperCase() != "GET" && method.toUpperCase() != "POST" )
			{
				throw new ArgumentError("Invalid method passed. Only GET and POST supported");
			}
			if( url.length == 0 )
			{
				throw new ArgumentError("Empty url passed");
			}

			var timeStamp:String = Math.round( new Date().getTime() / 1000 ).toString();
			var nonce:String = Math.round( Math.random() * 10000 ).toString();


			// Setup the standard parameters
			var newQueryString:QueryString = new QueryString();
			newQueryString.add( new Parameter( "oauth_version", "1.0" ) );
			newQueryString.add( new Parameter( "oauth_signature_method", "HMAC-SHA1" ) );
			newQueryString.add( new Parameter( "oauth_nonce", nonce ) );
			newQueryString.add( new Parameter( "oauth_timestamp", timeStamp ) );
			newQueryString.add( new Parameter( "oauth_consumer_key", _consumerKey ) );
			newQueryString.add( new Parameter( "oauth_token", _accessToken ) );

			// Add any post data if available
			//if( method.toUpperCase() == "POST" && postData.length )
			//{
				var qs:QueryString = new QueryString( postData );
				if( qs.length )
				{
					for( var p:uint = 0; p < qs.length; p++ )
					{
						newQueryString.add( qs.getParam( p ) );
					}
				}
			//}

			newQueryString.sort();	// Oauth requires parameters to be sorted


			var signedURI:String = newQueryString.toString();
//			trace( "signedURI: " + signedURI );

			var sigBase:String = method.toUpperCase() + "&" + encodeURIComponent( url ) + "&" + encodeURIComponent( signedURI );
//			trace( "sigBase: " + sigBase );

			var hmac:HMAC = Crypto.getHMAC( "sha1" );	// Must be lower case crypto type
			var key:ByteArray = Hex.toArray( Hex.fromString( encodeURIComponent(_consumerSecret) + "&" + encodeURIComponent(_accessTokenSecret) ) );
			var data:ByteArray = Hex.toArray( Hex.fromString( sigBase ) );

			var sha:String = Base64.encodeByteArray( hmac.compute( key, data ) );

			signedURI += "&oauth_signature=" + encodeURIComponent( sha );
//			trace( "signedURI: " + signedURI );

			return signedURI;
		}
		
		public function get state():int
		{
			return _state;
		}
	}
}